从18年底开始,公司的服务器经常受到各种挖矿脚本病毒的公司,Java后端Redis漏洞层出不穷,Hadoop这边MR的提交权限BUG也被利用了,于是决定调研Kerberos,发现Kerberos是一个巨大的坑,在此记录下笔记,作为我的Github Pages第一篇文档,希望后来人少走弯路。此文可能分为几次更新。
第一次更新:2019-4-29
第二次更新:2019-5-10
1.Kerberos 入门
Kerberos是一种计算机网络授权协议,用来在非安全网络中,对个人通信以安全的手段进行身份认证。Hadoop集群中涉及的Kerberos一般是指MIT基于Kerberos协议开发的一套软件。

Kerberos在希腊神话中是Hades的一条凶猛的三头保卫神犬。这三个头在Kerberos代表了Client、Server和KDC。
Kerberos的特点:并不要求通信双方所在的网络环境安全,即使通信过程中数据被截取或者篡改,依然不会影响整套机制的正常工作。时间戳是Kerberos用来保证通信安全的重要手段。
Kerberos的基本思路:基于对称加密,利用集中的认证服务器,实现用户和服务器之间的双向认证。(提供一种不能伪造、不能重放、已经鉴别的票据对象)。
2.Kerberos涉及名词
Principal:认证的主体,简单来说也就是 用户名
Kinit:Kerberos认证(登录)命令,可以使用密码或者KEYTAB
realm:有点类似namespace,一个principle只有在某个realm下才有意义
Password:某个用户的密码,对应于Kerberos中的master key。password可以存在一个KEYTAB文件中。所以Kerberos中需要使用密码的场景都可以用一个KEYTAB作为输入
credential:credential是“证明某个人确定是他自己/某一种行为的确可以发生”的凭据。在不同的使用场景下, credential的具体含义也略有不同:
对于某个principal个体而言,他的credential就是他的password
在Kerberos认证的环节中,credential就意味着各种各样的ticket
TGT:Ticket Granting Ticket,要获得key还需要一个资格认证,获得这个资格认证的证明,叫做TGT
Long-term Key/Master Key:在Security的领域中,有的Key可能长期内保持不变,比如你在密码,可能几年都不曾改变,这样的Key、以及由此派生的Key被称为Long-term Key。对于Long-term Key的使用有这样的原则:被Long-term Key加密的数据不应该在网络上传输。原因很简单,一旦这些被Long-term Key加密的数据包被恶意的网络监听者截获,在原则上,只要有充足的时间,他是可以通过计算获得你用于加密的Long-term Key的——任何加密算法都不可能做到绝对保密。
Short-term Key/Session Key:由于被Long-term Key加密的数据包不能用于网络传送,所以我们使用另一种Short-term
Key来加密需要进行网络传输的数据。由于这种Key只在一段时间内有效,即使被加密的数据包被黑客截获,等他把Key计算出来的时候,这个Key早就已经过期了。
3.Kerberos原理
最简单的对称加密的思路:A发送信息给B,信息分为两段,一段是明文,一段是加密之后的密文,这个密钥只有A和B两个人知道,B收到信息后,用两个人都知道的密钥破解了这段密文,如果破解之后的内容和明文内容一致就说明 A的身份没有问题。
Kerberos就是基于此基础之上的一套复杂的认证机制。
下面对整个认证过程进行一个细致的分析,对于安装部署过程中纠错来说(尤其CDH集群集成的Kerberos有一些问题),这个过程是非常有必要的:
通过第二节介绍的两种Key,让被认证的一方提供一个仅限于他和认证方知晓的Key来鉴定对方的真实身份。而被这个Key加密的数据包需要在Client和Server之间传送,所以这个Key不能是一个Long-term Key,而只可能是hort-term Key,这个可以仅仅在Client和Server的一个Session中有效,所以我们称这个Key为Client和Server之间的Session Key(Sserver-Client)。
这个Sserver-Client 需要引入Kerberos中的一个十分重要的觉得:Kerberos Distribution Center-KDC。KDC在整个Kerberos认证流程中作为Client和Server共同信任的第三方起着重要的作用,而Kerberos的认证过程就是通过这三方协作完成。KDC中维护着一个存储着该Domain中所有账户的Account Database(一个轻量级的数据库),这个数据库汇中存储着每个Account的名称和派生于该Account Password的Master Key,派生手段一般来说是类似Hash这种,不可逆的,然后可以一一对应的方法。
稍微扩展一下上面的key的分发过程:
首先是Client向KDC发送一个对SServer-Client的申请。这个申请的内容可以简单概括为“我是某个Client,我需要一个Session Key用于访问某个Server ”。KDC在接受了这个请求以后,生成一个Session Key,为了保证这个Session Key仅仅限于发送请求的Client和他希望访问的Server知晓,KDC会为这个Session Key生成两个Copy,分别被Client和Server使用。然后从Account database中提取Client和Server的Master Key分别对这两个Copy进行对称加密。对于后者,和Session Key一起被加密的还包含关于Client的一些信息。(这里的Client可以理解为发送连接请求的节点,Server可以理解为Client发送请求的接受节点)。
现在KDC有了两个分别被Master和Server的Master key加密过的Session Key,下面介绍这两个Session Key的处理方式。
Kerberos 并不会直接把两个加密包分别发送给Client和Server,原因主要有两个:
第一:由于一个Server会面对若干不同的Client, 而每个Client都具有一个不同的Session Key。那么Server就会为所有的Client维护这样一个Session Key的列表,这样对于Server来说是比较麻烦而低效的。
第二:由于网络传输的不确定性,可能出现这样一种情况:Client很快获得Session Key,并将这个Session Key作为Credential随同访问请求发送到Server,但是用于Server的Session Key确还没有收到,并且很有可能承载这个Session Key的永远也到不了Server端,Client将永远得不到认证。
为了解决这个问题,Kerberos的做法是:将这两个被加密的Copy一并发送给Client,属于Server的那份由Client发送给Server。
Client实际上获得了两组信息:一个通过自己Master Key加密的Session Key,另一个被Server的Master Key加密的数据包,包含Session Key和关于自己的一些确认信息。通过一个双方知晓的Key就可以对对方进行有效的认证,但是在一个网络的环境中,这种简单的做法是具有安全漏洞,为此,Client需要提供更多的证明信息,我们把这种证明信息称为Authenticator,在Kerberos的Authenticator实际上就是关于Client的一些信息和当前时间的一个Timestamp。
Client通过自己的Master Key对KDC加密的Session Key进行解密从而获得Session Key,随后创建**Authenticator(Client Info + Timestamp)并用Session Key对其加密。最后连同从KDC获得的、被Server的Master Key加密过的数据包(Client Info + Session Key)**一并发送到Server端。我们把通过Server的Master Key加密过的数据包称为Session Ticket。当Server接收到这两组数据后,先使用他自己的Master Key对Session Ticket进行解密,从而获得Session Key。随后使用该Session Key解密Authenticator,通过比较Authenticator中的Client Info和Session Ticket中的Client Info从而实现对Client的认证。
这里涉及到了一个Timestamp,Client向Server发送的数据包如果被某个恶意网络监听者截获,该监听者随后将数据包作为自己的Credential冒充该Client对Server进行访问,在这种情况下,依然可以很顺利地获得Server的成功认证。为了解决这个问题,Client在Authenticator中会加入一个当前时间的Timestamp。
在Server对Authenticator中的Client Info和Session Ticket中的Client Info进行比较之前,会先提取Authenticator中的Timestamp,并同当前的时间进行比较,如果他们之间的偏差超出一个可以接受的时间范围(一般是5mins),Server会直接拒绝该Client的请求。在这里需要知道的是,Server维护着一个列表,这个列表记录着在这个可接受的时间范围内所有进行认证的Client和认证的时间。对于时间偏差在这个可接受的范围中的Client,Server会从这个列表中获得最近一个该Client的认证时间,只有当Server接收到Authenticator时,验证Authenticator中的Timestamp,确定传输时间小于接受范围后,Server才采用进行后续的认证流程。
Time Synchronization的重要性
上述基于Timestamp的认证机制只有在Client和Server端的时间保持同步的情况才有意义。所以保持Time Synchronization在整个认证过程中显得尤为重要。在一个Domain中,一般通过访问同一个Time Service获得当前时间的方式来实现时间的同步。
双向认证(Mutual Authentication)
Kerberos一个重要的优势在于它能够提供双向认证:不但Server可以对Client 进行认证,Client也能对Server进行认证。
具体过程是这样的,如果Client需要对他访问的Server进行认证,会在它向Server发送的Credential中设置一个是否需要认证的Flag。Server在对Client认证成功之后,会把Authenticator中的Timestamp提出来,通过Session Key进行加密,当Client接收到并使用Session Key进行解密之后,如果确认Timestamp和原来的完全一致,那么他可以认定Server正试图访问的Server。
那么为什么Server不直接把通过Session Key进行加密的Authenticator原样发送给Client,而要把Timestamp提取出来加密发送给Client呢?原因在于防止恶意的监听者通过获取的Client发送的Authenticator冒充Server获得Client的认证。
More:
通过上面的介绍,我们发现Kerberos实际上一个基于Ticket的认证方式。Client想要获取Server端的资源,先得通过Server的认证;而认证的先决条件是Client向Server提供从KDC获得的一个有Server的Master Key进行加密的Session Ticket(Session Key + Client Info)。可以这么说,Session Ticket是Client进入Server领域的一张门票。而这张门票必须从一个合法的Ticket颁发机构获得,这个颁发机构就是Client和Server双方信任的KDC, 同时这张Ticket具有超强的防伪标识:它是被Server的Master Key加密的。对Client来说, 获得Session Ticket是整个认证过程中最为关键的部分。
我了解到这儿感觉已经差不多了,然而这还只是Kerbeos的梗概 T_T
Client要获得Ticket之前,还需要一个步骤,即获得KDC的权限确认,这个过程叫做TGT:TicketGranting Ticket。TGT的分发方仍然是KDC。首先Client向KDC发起对TGT的申请,申请的内容大致可以这样表示:“我需要一张TGT用以申请获取用以访问所有Server的Ticket”。KDC在收到该申请请求后,生成一个用于该Client和KDC进行安全通信的Session Key(SKDC-Client)。为了保证该Session Key仅供该Client和自己使用,KDC使用Client的Master Key和自己的Master Key对生成的Session Key进行加密,从而获得两个加密的SKDC-Client的Copy。对于后者,随SKDC-Client一起被加密的还包含以后用于鉴定Client身份的关于Client的一些信息。最后KDC将这两份Copy一起发送给Client。这里有一点需要注意的是:为了免去KDC对于基于不同Client的Session Key进行维护的麻烦,就像Server不会保存Session Key(SServer-Client)一样,KDC也不会去保存这个Session Key(SKDC-Client),而选择完全靠Client自己提供的方式。
当Client收到KDC的两个加密数据包之后,先使用自己的Master Key对第一个Copy进行解密,从而获得KDC和Client的SessionKey(SKDC-Client),并把该Session 和TGT进行缓存。有了Session Key和TGT,Client自己的MasterKey将不再需要,因为此后Client可以使用SKDC-Client向KDC申请用以访问每个Server的Ticket。同时需要注意的是SKDC-Client是一个Session Key,他具有自己的生命周期,同时TGT和Session相互关联,当Session Key过期,TGT也就宣告失效,此后Client不得不重新向KDC申请新的TGT,KDC将会生成一个不同Session Key和与之关联的TGT。同时,由于Client Log off也导致SKDC-Client的失效,所以SKDC-Client又被称为Logon Session Key。**TGT和Ticket有个区别就是Ticket是基于某个具体的Server的,而TGT则是和具体的Server无关的。**
Client在获得自己和KDC的Session Key(SKDC-Client)之后,生成自己的Authenticator以及所要访问的Server名称的并使用SKDC-Client进行加密。随后连同TGT一起发送给KDC。KDC使用自己的Master Key对TGT进行解密,提取Client Info和Session Key(SKDC-Client),然后使用这个SKDC-Client解密Authenticator获得Client Info,对两个Client Info进行比较进而验证对方的真实身份。验证成功,生成一份基于Client所要访问的Server的Ticket给Client,然后继续上面之说的过程。
介绍了这么多,重新把整个过程理一遍:
现在介绍的整个Authentication过程大概分为三个子过程
Client向KDC申请TGT(Ticket Granting Ticket)。
Client通过获得TGT向DKC申请用于访问Server的Ticket。
Client最终向为了Server对自己的认证向其提交Ticket。
整个Kerberos Authentication认证过程通过3个sub-protocol来完成:
Authentication Service ExchangeTicket Granting Service ExchangeClient/Server Exchange
下面内容来自官方文档的翻译:
1.Authentication Service Exchange
Client向KDC的Authentication Service发送Authentication Service Request(KRB_AS_REQ), 为了确保KRB_AS_REQ仅限于自己和KDC知道,Client使用自己的Master Key对KRB_AS_REQ的主体部分进行加密(KDC可以通过Domain 的Account Database获得该Client的Master Key)。KRB_AS_REQ的大体包含以下的内容:
Pre-authentication data:包含用以证明自己身份的信息。说白了,就是证明自己知道自己声称的那个account的Password。一般地,它的内容是一个被Client的Master key加密过的Timestamp。Client name&realm: 简单地说就是Domain name\ClientServer Name:注意这里的Server Name并不是Client真正要访问的Server的名称,而我们也说了TGT是和Server无关的(Client只能使用Ticket,而不是TGT去访问Server)。这里的Server Name实际上是KDC的Ticket Granting Service的Server Name。
AS(Authentication Service)通过它接收到的KRB_AS_REQ验证发送方的是否是在Client name & realm中声称的那个人,也就是说要验证发送方是否知道Client的Password。所以AS只需从Account Database中提取Client对应的Master Key对Pre-authentication data进行解密,如果是一个合法的Timestamp,则可以证明发送方提供的是正确无误的密码。验证通过之后,AS将一份Authentication Service Response(KRB_AS_REP)发送给Client。KRB_AS_REQ主要包含两个部分:本Client的Master Key加密过的Session Key(SKDC-Client:Logon Session Key)和被自己(KDC)加密的TGT。而TGT大体又包含以下的内容:
Client name & realm: 简单地说就是Domain name\ClientClient name & realm: 简单地说就是Domain name\ClientEnd time:TGT到期的时间
Client通过自己的Master Key对第一部分解密获得Session Key(SKDC-Client:Logon Session Key)之后,携带着TGT便可以进入下一步:TGS(Ticket Granting Service)Exchange。
2.Ticket Granting Service Exchange
TGS(Ticket Granting Service)Exchange通过Client向KDC中的TGS(Ticket Granting Service)发送Ticket Granting Service Request(KRB_TGS_REQ)开始。KRB_TGS_REQ大体包含以下的内容:
TGT:Client通过AS Exchange获得的Ticket Granting Ticket,TGT被KDC的Master Key进行加密。
Authenticator:用以证明当初TGT的拥有者是否就是自己,所以它必须以TGT的办法方和自己的Session Key(SKDC-Client:Logon Session Key)来进行加密。
Client name & realm: 简单地说就是Domain name\Client。
Server name & realm: 简单地说就是Domain name\Server,这回是Client试图访问的那个Server。
TGS收到KRB_TGS_REQ在发给Client真正的Ticket之前,先得整个Client提供的那个TGT是否是AS颁发给它的。于是它不得不通过Client提供的Authenticator来证明。但是Authentication是通过Logon Session Key(SKDC-Client)进行加密的,而自己并没有保存这个Session Key。所以TGS先得通过自己的Master Key对Client提供的TGT进行解密,从而获得这个Logon Session Key(SKDC-Client),再通过这个Logon Session Key(SKDC-Client)解密Authenticator进行验证。验证通过向对方发送Ticket GrantingService Response(KRB_TGS_REP)。这个KRB_TGS_REP有两部分组成:使用Logon Session Key(SKDC-Client)加密过用于Client和Server的Session Key(SServer-Client)和使用Server的Master Key进行加密的Ticket。该Ticket大体包含以下一些内容:
Client name & realm: 简单地说就是Domain name\Client
Client name & realm: 简单地说就是Domain name\Client
End time: Ticket的到期时间
Client收到KRB_TGS_REP,使用Logon Session Key(SKDC-Client)解密第一部分后获得Session Key(SServer-Client)。有了Session Key和Ticket,Client就可以之间和Server进行交互,而无须在通过KDC作中间人了。所以我们说Kerberos是一种高效的认证方式,它可以直接通过Client和Server双方来完成,不像Windows NT 4下的NTLM认证方式,每次认证都要通过一个双方信任的第3方来完成。
我们现在来看看 Client如果使用Ticket和Server怎样进行交互的,这个阶段通过我们的第3个Sub-protocol来完成:CS(Client/Server )Exchange。
- CS(Client/Server )Exchange
这个已经经介绍过。Client通过TGS Exchange获得Client和Server的Session Key(SServer-Client),随后创建用于证明自己就是Ticket的真正所有者的Authenticator,并使用Session Key(SServer-Client)进行加密。最后将这个被加密过的Authenticator和Ticket作为Application Service Request(KRB_AP_REQ)发送给Server。除了上述两项内容之外,KRB_AP_REQ还包含一个Flag用于表示Client是否需要进行双向验证(Mutual Authentication)。
Server接收到KRB_AP_REQ之后,通过自己的Master Key解密Ticket,从而获得Session Key(SServer-Client)。通过Session Key(SServer-Client)解密Authenticator,进而验证对方的身份。验证成功,让Client访问需要访问的资源,否则直接拒绝对方的请求。
对于需要进行双向验证,Server从Authenticator提取Timestamp,使用Session Key(SServer-Client)进行加密,并将其发送给Client用于Client验证Server的身份。
以上是2000年的Kerberos技术,和今天我们使用的Kerberos是不太相同的,因为这样的一个认证过程有一个最大的隐患就是Long-term Key加密的数据在网络中传递。
解决办法也很简单:就是采用一个Short-term的Session Key,而不是Server Master Key对Ticket进行加密。这就是Kerberos的第四个Sub-protocol:User2User Protocol。
因为KDC是不是维护Session Key的,所以这个Session key只能靠申请Ticket的Client提供,所以在原先的第一步和第二步之间,Client还得对Server进行请求已获得Server和KDC之间的Session Key。而对于Server来说,他可以像Client一样通过AS Exchange获得他和KDC之间的Session Key(SKDC-Server)和一个封装了这个Session Key并被KDC的Master Key进行加密的TGT,一旦获得这个TGT,Server会缓存它,以待Client对它的请求。
所以现在添加完这个User2User的认证过程,这个过程有4个步骤组成,四个步骤如下:
AS Exchange:Client通过此过程获得了属于自己的TGT,有了此TGT,Client可凭此向KDC申请用于访问某个Server的Ticket。
User2User:这一步的主要任务是获得封装了Server和KDC的Session Key(SKDC-Server)的属于Server的TGT。如果该TGT存在于Server的缓存中,则Server会直接将其返回给Client。否则通过AS Exchange从KDC获取。
TGS Exchange:Client通过向KDC提供自己的TGT,Server的TGT以及Authenticator向KDC申请用于访问Server的Ticket。KDC使用先用自己的Master Key解密Client的TGT获得SKDC-Client,通过SKDC-Client解密Authenticator验证发送者是否是TGT的真正拥有者,验证通过再用自己的Master Key解密Server的TGT获得KDC和Server 的Session Key(SKDC-Server),并用该Session Key加密Ticket返回给Client。
C/S Exchange:Client携带者通过KDC和Server 的Session Key(SKDC-Server)进行加密的Ticket和通过Client和Server的Session Key(SServer-Client)的Authenticator访问Server,Server通过SKDC-Server解密Ticket获得SServer-Client,通过SServer-Client解密Authenticator实现对Client的验证。
4.Kerberos的安装和Apach原生HDFS的配置
环境:
- Linux版本:CentOS Linux release 7.2.1511 (Core)
- CDH版本:5.13.3
- JDK版本:jdk1.8.0_144
- 运行用户:root
准备工作:
确认添加主机名解析到/etc/hosts 文件中
1 | 172.16.0.3 master |
hostname 请使用小写,要不然在集成Kerberos 时会出现一些错误。
安装Kerberos:
在master上安装包 krb5、krb5-server 和krb5-client。
yum install krb5-server -y
在所有节点上安装krb5-devel、krb5-workstation:
yum install krb5-devel krb5-workstation -y
修改配置文件
Kerberos的配置文件需要修改三个
/etc/krb5.conf
/var/kerberos/krb5kdc/kdc.conf
/var/kerberos/krb5kdc/kadm5.acl
配置Kerberos的krb5.conf
官网样例:
1 | [libdefaults] |
说明:样例来自MIT官网,部分配置选项含义如下:
[logging]:表示 server 端的日志的打印位置
[libdefaults]:每种连接的默认配置,需要注意以下几个关键的小配置
default_realm = EXAMPLE.COM:设置 Kerberos 应用程序的默认领域。如果您有多个领域,只需向 [realms] 节添加其他的语句。
ticket_lifetime: 表明凭证生效的时限,一般为24小时。
renew_lifetime: 表明凭证最长可以被延期的时限,一般为一个礼拜。当凭证过期之后,对安全认证的服务的后续访问则会失败。
clockskew:时钟偏差是不完全符合主机系统时钟的票据时戳的容差,超过此容差将不接受此票据。通常,将时钟扭斜设置为 300 秒(5 分钟)。这意味着从服务器的角度看,票证的时间戳与它的偏差可以是在前后 5 分钟内。
udp_preference_limit= 1:禁止使用 udp 可以防止一个 Hadoop 中的错误
[realms]:列举使用的 realm。
kdc:代表要 kdc 的位置。格式是 机器:端口
admin_server:代表 admin 的位置。格式是 机器:端口
default_domain:代表默认的域名
[appdefaults]:可以设定一些针对特定应用的配置,覆盖默认配置。
经过一段时间的对比实验,最终配置文件设置为:
1 | [logging] |
接下来是第二个配置文件/var/kerberos/krb5kdc/kdc.conf:
1 | [kdcdefaults] |
这边直接放上配置文件,官方的配置文件相当冗余,加上版本问题,我删除了很多选项,只留下了小部分,剩下的应该会以默认值运行。
贴上一点简单的选项说明:
EXAMPLE.COM: 是设定的 realms。名字随意。Kerberos 可以支持多个 realms,会增加复杂度。大小写敏感,一般为了识别使用全部大写。这个 realms 跟机器的 host 没有大关系。
master_key_type:和 supported_enctypes 默认使用 aes256-cts。JAVA 使用 aes256-cts 验证方式需要安装 JCE 包,见下面的说明。为了简便,你可以不使用 aes256-cts 算法,这样就不需要安装 JCE 。
acl_file:标注了 admin 的用户权限,需要用户自己创建。文件格式是:Kerberos_principal permissions [target_principal] [restrictions]
supported_enctypes:支持的校验方式。
admin_keytab:KDC 进行校验的 keytab。
这边要注意,如果系统是Centos5.6及以上系统,默认使用AES-256来加密,但是这个AES-256 JDK的安全包里默认不存在,所以要去Oracle下载,但是这个密码增强包貌似对JDK过高的版本支持有BUG,但是暂时好像还没遇到,下载下来之后放到这个目录里:$JAVA_HOME/jre/lib/security
还有一个文件/var/kerberos/krb5kdc/kadm5.acl:
这个是权限控制文件,修改为
*/admin@JIMI.COM *
这三个配置文件中只有krb5.conf需要拷贝到集群中其他服务器
别的两个配置文件不需要分发到别的节点。
创建数据库:
原理里面已经讲过了KDC里面有一个小型的数据库,下面是对这个数据库的操作。
在master上运行初始化数据库命令,其中 -r 指定对应的realm
kdb5_util create -r JIMI.COM -s
出现 loading random data 的时候另开个终端执行点消耗CPU的命令如cat /dev/sda > /dev/urandom 可以加快随机数采集。该命令会在/var/kerberos/krb5kdc/ 目录下创建 principal 数据库。
如果遇到数据库已经存在的提示,可以把 /var/kerberos/krb5kdc/ 目录下的 principal 的相关文件都删除掉。默认的数据库名字都是 principal。可以使用 -d 指定数据库名字。
这个数据库相当重要,后面还会介绍。
启动服务:
在master节点上运行:
centos 6:
1 | chkconfig krb5kdc on |
centos 7:
1 | systemctl start krb5kdc |
创建Kerberos管理员
Kerberos的管理,有两个方式,分别是kadmin.local 或 kadmin,至于使用哪个,取决于账户和权限访问。
如果有访问 kdc 服务器的 root 权限,但是没有 kerberos admin 账户,使用 kadmin.local
如果没有访问 kdc 服务器的 root 权限,但是用 kerberos admin 账户,使用 kadmin
在master上创建远程管理的程序员:
#手动输入两次密码,这里密码为 root
kadmin.local -q "addprinc root/admin"
#也可以不用手动输入密码
echo -e "root\nroot" | kadmin.local -q "addprinc root/admin"
#或者运行下面命令
1 | kadmin.local <<eoj |
系统时提示输入密码,密码不能为空,而且需要妥善保管。
测试Kerberos:
查看当前认证用户
#查看 principals
kadmin: list_principals
#添加一个新的principal
1 | kadmin: addprinc user1 |
#删除 principal
1 | kadmin: delprinc user1 |
也可以直接通过下面的命令来执行
#提示需要输入密码
1 | kadmin -p root/admin -q "list_principals" |
#不用输入密码
1 | kadmin.local -q "list_principals" |
#创建一个测试用户test,密码设置为test:
echo -e "test\ntest" | kadmin.local -q "addprinc test"
#获取test用户的ticket 通过用户名和密码登录
1 | kinit test |
销毁test用户的ticket
1 | kdestroy |
更新ticket
1 | kinit root/admin |
抽取密钥并将其储存在本地 keytab 文件 /etc/krb5.keytab 中。这个文件由超级用户拥有,所以您必须是 root 用户才能在 kadmin shell 中执行以下命令:
1 | kadmin.local -q "ktadd kadmin/admin" |
HDFS上配置kerberos
创建认证规则
在 Kerberos 安全机制里,一个 principal 就是 realm 里的一个对象,一个 principal 总是和一个密钥(secret key)成对出现的。
这个 principal 的对应物可以是 service,可以是 host,也可以是 user,对于 Kerberos 来说,都没有区别。
Kdc(Key distribute center) 知道所有 principal 的 secret key,但每个 principal 对应的对象只知道自己的那个 secret key 。这也是“共享密钥“的由来。
对于 hadoop,principals 的格式为
username/fully.qualified.domain.name@YOUR-REALM.COM
通过 yum 源安装的 cdh 集群中,NameNode和 DataNode 是通过 hdfs 启动的,故为集群中每个服务器节点添加两个principals:hdfs、HTTP。
在 KCD server 上(这里是 cdh1)创建 hdfs principal:
1 | kadmin.local -q "addprinc -randkey hdfs/datanode0@JIMI.COM" |
-randkey
标志没有为新 principal 设置密码,而是指示 kadmin 生成一个随机密钥。之所以在这里使用这个标志,是因为此 principal 不需要用户交互。它是计算机的一个服务器账户。
1 | kadmin.local -q "addprinc -randkey HTTP/ datanode0@JIMI.COM " |
创建完成后,查看:
kadmin.local -q "listprincs"
手动创建keytab文件
keytab是包含 principals 和加密 principal key 的文件。keytab 文件对于每个 host是唯一的,因为 key 中包含 hostname。keytab文件用于不需要人工交互和保存纯文本密码,实现到 kerberos 上验证一个主机上的 principal。因为服务器上可以访问 keytab 文件即可以以 principal 的身份通过 kerberos 的认证,所以,keytab 文件应该被妥善保存,应该只有少数的用户可以访问。
创建包含 hdfs principal 和 host principal 的 hdfs keytab:
1 | xst -norandkey -k hdfs.keytab hdfs/fully.qualified.domain.name host/fully.qualified.domain.name |
创建包含 mapred principal 和 host principal 的 mapred keytab:
1 | xst -norandkey -k mapred.keytab mapred/fully.qualified.domain.name host/fully.qualified.domain.name |
注意:上面的方法使用了xst的norandkey参数,有些kerberos不支持该参数。
当不支持该参数时有这样的提示:Principal -norandkey does not exist.,需要使用下面的方法来生成keytab文件:
在master节点,即KDC server节点上执行下面的命令:
1 | cd /var/kerberos/krb5kdc/ |
这样,就会在 /var/kerberos/krb5kdc/ 目录下生成hdfs-unmerged.keytab 和 HTTP.keytab 两个文件,接下来使用 ktutil 合并者两个文件为 hdfs.keytab。
1 | cd /var/kerberos/krb5kdc/ |
使用 klist 即可查看 hdfs.keytab 文件列表:(省略)
验证是否正确合并了key,使用合并后的keytab,分别使用hdfs和host principals来获取证书。
1 | kinit -k -t hdfs.keytab hdfs/master@JIMI.COM |
如果出现错误:kinit: Key table entry not found while getting initial credentials,则上面的合并有问题,重新执行前面的操作。
部署kerberos keytab文件
拷贝 hdfs.keytab 文件到其他节点的 /etc/hadoop/conf 目录
并设置权限,分别在三个节点上运行
1 | chown hdfs:hadoop /etc/hadoop/conf/hdfs.keytab |
原因:
由于 keytab 相当于有了永久凭证,不需要提供密码(如果修改kdc中的principal的密码,则该keytab就会失效),所以其他用户如果对该文件有读权限,就可以冒充 keytab 中指定的用户身份访问 hadoop,所以 keytab 文件需要确保只对 owner 有读权限(0400)
修改hdfs配置文件,先停止集群
在集群总所有节点的core-site.xml文件中添加下面的配置:
1 | <property> |
在集群总所有节点的hdfs-site.xml文件中添加下面的配置:
1 | <property> |
如果想开启 SSL,请添加(本文不对这部分做说明):
1 | <property> |
如果 HDFS 配置了 QJM HA,则需要添加(另外,你还要在 zookeeper 上配置 kerberos):
1 | <property> |
如果配置了WebHDFS,则添加:
1 | <property> |
配置中需要注意的点:
1.dfs.datanode.address表示data transceiver RPC server所绑定的hostname或IP地址,如果开启 security,端口号必须小于 1024(privileged port),否则的话启动 datanode 时候会报 Cannot start secure cluster without privileged resources 错误。
2.principal 中的 instance 部分可以使用 _HOST 标记,系统会自动替换它为全称域名。
3.如果开启了 security, hadoop 会对 hdfs block data(由 dfs.data.dir 指定)做 permission check,方式用户的代码不是调用hdfs api而是直接本地读block data,这样就绕过了kerberos和文件权限验证,管理员可以通过设置 dfs.datanode.data.dir.perm 来修改 datanode 文件权限,这里我们设置为700
CDH的权限管理(来自cloudera官网)
1 | hdfs:NameNode, DataNodes, and Secondary NameNode |
目录:
1 | dfs.name.dir/ hdfs:hadoop |
启动NameNode
启动之前必须确保JCE jar已经替换,首先检查JSVC
首先master节点查看是否安装了JSVC
1 | ls /usr/lib/bigtop-utils/ |
然后编辑/etc/default/hadoop-hdfs-datanode,取消对下面注释并添加一行JSVC_HOME,修改如下:
1 | export HADOOP_SECURE_DN_USER=hdfs |
hadoop-hdfs-datanode同步到其他节点
随后分别在CDH2、CDH3获取ticket然后启动服务
#root为root/admin密码
kinit -k -t /etc/hadoop/conf/hdfs.keytab hdfs/master@JIMI.COM; service hadoop-hdfs-datanode start
(这仅仅为master节点上的操作,别的节点类似)
观察master上的Namenode日志,出现下面的日志表名Datanode启动成功
1 | 14/11/04 17:21:41 INFO security.UserGroupInformation: |
Tips:
配置 hosts,hostname 请使用小写
确保 kerberos 客户端和服务端连通
替换 JRE 自带的 JCE jar 包
为 DataNode 设置运行用户并配置 JSVC_HOME
启动服务前,先获取 ticket 再运行相关命令
Quote:
5.Apach 原生Zookeeper的Kerberos配置及其验证
Zookeeper的配置分为两个步骤,先配置Server的keytab,再配置Client的keytab,如果zookeeper-client 和 zookeeper-server 安装在同一个节点上,则 java.env 中的 java.security.auth.login.config 参数会被覆盖,这一点从zookeeper-client 命令启动日志可以看出来。
首先是配置 Zk Server
因为KDC已经配置了,所以KDC不用再配
第一步直接生成Keytab
1 | cd /var/kerberos/krb5kdc/ |
将Keytab拷贝到目录/etc/zookeeper/conf
在三个节点上分别执行
1 | cd /etc/zookeeper/conf/;chown zookeeper:hadoop zookeeper.keytab ;chmod 400 *.keytab |
目的是为了控制权限。
然后修改配置文件
在三个节点上的zoo.cfg文件中添加
1 | authProvider.1=org.apache.zookeeper.server.auth.SASLAuthenticationProvider jaasLoginRenew=3600000 |
然后创建JAAS配置文件
1 | Server { |
三个节点都要有,每个节点里面的principal有点不同
然后是配置Zookeeper Client
还是先生成keytab
1 | cd /var/kerberos/krb5kdc/ |
拷贝 zkcli.keytab 文件到其他节点的 /etc/zookeeper/conf 目录,并设置权限,分别在master、datanode0、datanode1 上执行:
1 | cd /etc/zookeeper/conf/; |
由于 keytab 相当于有了永久凭证,不需要提供密码(如果修改 kdc 中的 principal 的密码,则该 keytab 就会失效),所以其他用户如果对该文件有读权限,就可以冒充 keytab 中指定的用户身份访问 hadoop,所以 keytab 文件需要确保只对 owner 有读权限(0400)
创建 JAAS 配置文件
在/etc/zookeeper/conf/创建client-jaas.conf文件,内容如下
1 | Client { |
同步到其他节点,然后在/etc/zookeeper/conf/目录创建或者修改java.env,内容如下
export CLIENT_JVMFLAGS="-Djava.security.auth.login.config=/etc/zookeeper/conf/client-jaas.conf"
并且同步到别的节点上
接着是验证:
启动客户端:zookeeper-client -server master:2181
创建一个znode节点:
1 | [zk: master:2181(CONNECTED) 0] create /znode1 sasl:zkcli@JIMI.COM:cdwra |
验证该节点是否创建以及其ACL:
1 | [zk: master:2181(CONNECTED) 1] getAcl /znode1 |
只要能够在一个节点上创建znode,在别的节点上能够显示出来,就说明Zookeeper的kerberos已经配置成功。
6.Apach原生Kafka的Kerberos配置及其验证
因为之前的操作已经搭建完了KDC,所以省略了自建Kerberos的步骤
首先还是为broker每台服务器在Kerberos服务器生成相应的principal和Keytab,将下列命令里生成的kafka.keytab文件分发到对应broker机器的统一位置,比如/etc/kafka.keytab:
1 | addprinc -randkey kafka/kafkahost1@EXAMPLE.COM |
配置kafka server文件
1 | listeners=SASL_PLAINTEXT://:9092 |
KafkaClient模块是为了bin目录下kafka-console-consumer.sh之类的脚本使用的
1 | KafkaServer { |
修改bin目录下kafka-run-class.sh,在 exec $JAVA 后面增加kerberos启动参数,然后就可以用正常的脚本启动服务了:
1 | -Djava.security.krb5.conf=/etc/krb5.conf -Djava.security.auth.login.config=/opt/kafka/config/kafka_server_jaas.conf |
或者用这个脚本启动:
1 | !/bin/bash |
最终的目的都是一样
客户端脚本使用
启用kerberos后,部分kafka管理脚本需要增加额外的参数才能使用
首先建立配置文件client.properties
1 | security.protocol=SASL_PLAINTEXT |
涉及到的zookeeper.properties
1 | authProvider.1=org.apache.zookeeper.server.auth.SASLAuthenticationProvider |
所以新命令的使用方式为
1 | bin/kafka-consumer-groups.sh --bootstrap-server kafkahost1:9092 --list --command-config client.properties |
如果之前JCE的包没有安装好的话会报如下错误,需要把JCE包安装到位即可
1 | WARN [Controller-60-to-broker-60-send-thread], Controller 60's connection to broker kafka60:9092 (id: 60 rack: null) was unsuccessful (kafka.controller.RequestSendThread) |
能在命令行上运行成功命令行消费者和命令行生产者就说明ZK和Kafka的安装基本搞定,没有问题。
用Java连接集群上的Kerberos组件篇幅较长,涉及代码,另外开一篇文章说明。
7.Cloudera’s Distribution Including Apache Hadoop(CDH)上Kerberos的安装
CDH上面的Kerberos安装其实已经被简化了,简化的好处是安装方便,坏处是一旦出现问题不知从何处下手。所以对前面单独组件的了解是有必要的。实际操作下来,官网和各技术博客都有些许问题。在此记录。
首先配置KDC,和单独安装操作相同
首先KDC还是要配置,在CM服务器上配置KDC
1 | yum -y install krb5-serverkrb5-libs krb5-auth-dialog krb5-workstation |
修改/etc/krb5.conf、/var/kerberos/krb5kdc/kadm5.acl、/var/kerberos/krb5kdc/kdc.conf配置,配置内容相同,不再赘述。
然后在所有节点上(包括CM)安装Kerberos客户端
1 | yum -y install krb5-libs krb5-workstation |
然后在CM节点上独立安装额外的包
1 | yum -y install openldap-clients |
分发krb5.conf到另外两个节点
JCE包还是要记得替换,替换位置:/usr/share/java/jdk1.8.0_144/jre/lib/security
KDC安装完成后,建立测试Kerberos的管理员账号。

CM界面 ->管理 -> 安全

点击启用

全部勾选即可

这边别的都好理解,都是和host上面对应的,然后这个加密类型是和krb5.conf对应相同的

这上面有张图没截到,是选择是否要通过CM管理的,一般选择不通过
这边管理账户输入之前创建的管理账户即可。

这边CDH会帮助验证密码

接下来的这个步骤我标红了一块,我在这边spark2的部分,服务范围设置的是spark2,默认是spark,我修改了一下,避免以后的keytab名字出现歧义

随后,直接勾选重启选项即可
初步的配置就完成了
(包括了备份和传播Kerberos、如何恢复Kerberos、如何在服务器升级后转换Kerberos数据库,如何重新配置主KDC服务器以使用增量传播,如何重新配置从KDC以使用增量传播,如何配置从 KDC 服务器以使用完全传播,如何验证KDC服务器已经同步,如何手动将Kerberos数据库传播到从KDC服务器,设计并行传播,设置并行传播的配置步骤,管理存储文件,如何删除存储文件等。)
8.对Kerberos的一点使用心得
CDH的Kerberos其实算是相对好管理的,最起码组件的principal都是CDH自动生成的。
在KDC上创建完成管理员开启功能之后,我常用的操作有这些:
1 | 登录,通过创建的BDB管理账号管理 |
9.如何验证Kerberos已经安装完成
HDFS
登录到某一个节点后,切换到hdfs用户,然后用kinit来获取credentials
现在用hadoop hdfs -ls /应该能正常输出结果
用kdestroy销毁credentials后,再使用hadoop hdfs -ls /会发现报错
Kafka
用新的一套API,消费者能正常消费,生产者能正常生产不报错误就算ok
注意:是新API,开启Kerberos之后老API无法再使用
Zookeeper
启动zookeeper:
Zookeeper-client -server master:2181
创建一个 znode 节点:
create /znode1 sasl:master@JIMI.com:cdwra
在另外的节点上执行
getAcl /znode1
如果能够获取在另外一个节点上输入的输入就证明没有问题
10.Kerberos遇到的坑
Kerberos卸载BUG
Linux上的KDC存在严重的卸载BUG,使用yum remove卸载会出现严重的问题
因为之前principal的认证因为人为操作出现了一些问题,所以用yum remove卸载重新安装了一下,yum remove之后出现了大问题,凡是新连接外部的命令都无法使用,包括并不限于:yum、ssh、wgt等命令,FTP工具也无法使用,这就导致了无法连接外部下载kdc安装包
还好我卸载之前连接的SSH窗口没有关闭(新的连接无法建立, 但是已经建立的连接不会断开),用的XSHELL 6,我尝试用XFTP连接,失败,但是XSHELL 6 默认输入框里就有传输文件的功能,上传四个Kerberos文件之后重新安装之后才解决了问题。
Zookeeper报错
Zk这个组件启动的时候在互相连接的装一下会报Error,这个Error曾今困扰了我挺久
1 | 2019-02-26 18:37:15,898 WARN org.apache.zookeeper.server.NIOServerCnxn: caught end of stream exception |
其实只要Zk验证了没有问题就行了,这个报错不用纠结
Kafka启动/关闭Kerberos的坑
CDH集群中为Kafka启用Kerberos需要些配置之外的操作,启用Kerberos的时候容易忽略这些细节,导致kafka开启不正确, 然后关闭的时候容易把这些操作忽略了,导致关闭不彻底,在有的环节仍然关闭了Kerberos。
Kafka在CDH中的配置需要先登录CM进入kafka服务,修改ssl.client.auth为none,这届两个Kerberos相关的配置设为开启,接下来还要修改security.inter.broker.protocol配置为SASL_PLAINTEXT,保存以上修改的配置后,回到主页根据提示重启kafka Server,接下来就是在客户端上的配置,本身CDH就会为了Kafka生成配置文件jaas.conf,对于这个配置文件真实一言难尽,里面的配置文件会有 不起眼的错误(是关于KafkaClient和KafkaServer混淆的错误),这一个改正完毕,还有一个配置文件client.properties文件,两个文件设置完毕后,在/etc/profile里面设置环境变量export KAFKA_OPTS="-Djava.security.krb5.conf=/etc/krb5.conf -Djava.security.auth.login.config=/opt/kafka/kafka_client.jaas",配置完毕kafka这块,关闭的时候容易忘记,必须要记得从profile中删除才行。
Flume配置文件导致文件无限传输
Flume的配置文件出了错误,因为对Flume的KafkaChannel的不熟悉
配置的时候把Channel的sink又连接到Source上导致了数据一致循环。。。
排查之后发现了这个问题,附上Flume的配置文件
1 | agent.sources = kafkaSource1 |
例子中的配置文件的最终效果是从kafka的多个topic report.stop等等读取数据之后通过kafka channel,然后根据不同的topic生成不同的文件。
HUE中Oozie报错,时区错误
发现HUE的时间和实际时间有偏差,原因是HUE的时区默认是美国,要在配置里面修改,修改成东8区即可

系统自带的无法识别的配置文件
值得一提的是这边有个错误我花了好久才发现,CDH因为是高度集成的,里面很多配置文件都是自己生成的,像Kafka的Keytab配置文件,文件里面的内容并不正确,里面指定的KafkaClient和Server根本无法识别,更改之后才可以识别。
1 | KafkaClient { |
11.Windows下访问Kerberos CDH集群的WebUI界面
先安装windows下的Kerberos安装包,无脑安装就行了。
接着配置krb5..ini文件,将krb5.conf的内容拷贝进来,切忌不要直接更改后缀名就使用
接着启动MIT Kerberos软件
使用我们在linux KDC上注册的管理员账号登录即可。我们登录不同的服务使用到的不用的账号,这个软件貌似会通过我们这个管理员账号自己搞定。
还有一种方法,需要使用Keytab,还涉及到文件权限的问题,因为上面的方法我很轻易就成功访问了WebUI,所以第二种方法就没有尝试。
12.禁用Kerberos需要调整的设置
说明:设置可能会随着版本变化有所变化
Zookeeper
enableSecurity (Enable Kerberos Authentication): falsezoo.cfg的Server 高级配置代码段(安全阀)写入skipACL: yes
HDFS
hadoop.security.authentication: Simplehadoop.security.authorization: falsedfs.datanode.address: 1004 (for Kerberos) 改为 50010 (default)dfs.datanode.http.address: 1006 (for Kerberos) 改为 50075 (default)dfs.datanode.data.dir.perm: 700 改为 755
HBase
hbase.security.authentication: Simplehbase.security.authorization: falsehbase.thrift.security.qop: none
Hue
Kerberos Ticket Renewer: 删除或停用角色
Kafka
kerberos.auth.enable: false
SOLR
solr Secure Authentication: Simple
13.集群同步脚本
1 | !/bin/bash |
要先安装rsync
yum install rsync
安装成功之后才能用这个同步命令
14.Kerberos优化
美团优化实战
为什么要优化:
线上单台KDC服务器最大承受QPS是多少?哪台KDC的服务即将出现压力过大的问题?为什么机器的资源非常空闲,KDC的压力却会过大?如何优化?优化后瓶颈在哪儿?如何保证监控指标的全面性、可靠性和准确性?这都是本文需要回答的问题。从本次优化工作达成的最终结果上来看,单台服务器每秒的处理性能提升16倍左右,另外通过共享内存的方式设计了一个获取KDC各项核心指标的接口,使得服务的可用性进一步提升。
名词:

下图是美团的架构,整个KDC服务都部署在同一个IDC

主要优化工作
通过对KDC原理的分析,很容易判断只有前两部分才可能直接给KDC服务带来压力,因此本文涉及到的工作都将围绕上一部分的前两个环节展开分析。本次优化工作采用Grinder这一开源压测工具,分别对AS、TGS两个请求过程,采用相同机型(保证硬件的一致性)在不同场景下进行了压力测试。
优化之前,线上KDC服务启动的单进程;为最低风险的完成美团和点评数据的融合,KDC中keytab都开启了PREAUTH属性;承载KDC服务的部分服务器没有做RAID。KDC服务出现故障时,机器整体资源空闲,怀疑是单进程的处理能力达到上限;PREAUTH属性进一步保证提升了KDC服务的安全性,但可能带来一定的性能开销;如果线上服务器只加载了少量的keytab信息,那么没有被加载到内存的数据必然读取磁盘,从而带来一定的IO损耗。
因此本文中,对以下三个条件进行变动,分别进行了测试:
- 对承载KDC服务的物理机型是否做RAID10;
- 请求的keytab在库中是否带有PRAUTH属性;
- KDC是否启动多进程(多进程设置数目和物理机核数一致)。(实际测试工作中进行了多次测试)
Client和AS交互过程的压测
下图为AS压测的一组平均水平的测试数据,使用的物理机有40核,因此多进程测试启动40个进程。

分析表中的数据,很容易提出如下问题从而需要进一步探索:
- 比较表中第一行和第二行、第三行和第四行,主机做不做RAID为什么对结果几乎无影响?
该四组(测试结果为49、53、100和104所在表2中的行)数据均在达到处理能力上限一段时间后产生认证失败,分析机器的性能数据,内存、网卡、磁盘资源均没有成为系统的瓶颈,CPU资源除了某个CPU偶尔被打满,其他均很空闲。分析客户端和服务端的认证日志,服务端未见明显异常,但是客户端发现大量的Socket Timeout错误(测试设置的Socket超时时间为30s)。由于测试过程中,客户端输出的压力始终大于KDC的最大处理能力,导致KDC端的AS始终处于满负荷状态,暂时处理不了的请求必然导致排队;当排队的请求等待时间超过设置的30s后便会开始超时从而认证出错,且伴随机器某一CPU被打满(如图3)。 显然KDC单进程服务的处理能力已经达到瓶颈且瓶颈存在单核CPU的处理能力,从而决定向多进程方向进行优化测试。
单进程KDC打满某一CPU:

下图为本次压力测试的一个通用模型,假设KDC单位时间内的最大处理能力是A,来自客户端的请求速率稳定为B且 B>A ;图中黄色区域为排队的请求数,当某一请求排队超过30s,便会导致Socket Timedout错误。
AS处理能力和Client压力模型:

- 比较上上一张表中第1和3行、第2和4行、第7和8行相比,为什么有PREAUTH属性的认证QPS大致是无该属性处理能力的一半?
如果Client的keytab在KDC的库中不带有PREAUTH这一属性,Client发送请求,KDC的AS模块验证其合法性之后返回正确的结果;整个过程只需要两次建立链接进行交互便可完成。如果带有PREAUTH属性,意味着该keytab的认证启动了Kerberos 5协议中的 pre-authentication概念:当AS模块收到Client的请求信息后;故意给Client返回一个错误的请求包,Client会“领悟到”这是KDC的AS端需要进行提前认证;从而Client获取自己服务器的时间戳并用自己的密钥加密发送KDC,KDC解密后和自身所在服务器的时间进行对比,如果误差在能容忍的范围内;返回给Client正确的TGT响应包;过程如下图所示。

- 根据对问题2的分析,CPU占用表中第5和7行的值的比例应该近似为1:2,为什么第5行的值只有115,结果和理论差距如此之大?
KDC的库中对客户端的keytab开启PREAUTH属性,客户端每认证一次,KDC需要将该次认证的时间戳等信息写到本次磁盘的BDB数据库的Log中;而关闭PREAUTH属性后,每次认证只需要从库中读取数据,只要给BDB数据库分配的内存足够大,就可以最大程度的减少和本次磁盘的交互。KDC40进程且开启PRAUTH,其AS处理能力的QPS只有115,分析机器性能的相关指标,发现瓶颈果然是单盘的IO,如图6所示。使用BDB提供的工具,查看美团数据平台KDC服务的BDB缓存命中率为99%,如下图所示:
无RAID多KDC进程服务器磁盘IO:

美团KDC缓存命中率:

- KDC AS处理能力在多进程做RAID条件下,有无preauth属性,KDC服务是否有瓶颈?如果有在哪里?
经多次实验,KDC的AS处理能力受目前物理机CPU处理能力的限制,图8为有PREAUTH属性的CPU使用情况截图,无PREAUTH结果一致。
40进程有PREAUTH,AS对CPU资源的使用情况:

Client和TGS交互过程的压测:
下表为TGS压测的一组平均水平的测试数据:
TGS压测:

可以发现KDC对TGS请求的处理能力和主机是否做RAID无关,结合KDC中TGS的请求原理,就较容易理解在BDB缓存命中率足够高的条件下,TGS的请求不需要和本次磁盘交互;进一步做实验,也充分验证了这一点,机器的磁盘IO在整个测试过程中,没有大的变化,如图所示,操作系统本身偶尔产生的IO完全构不成KDC的服务瓶颈。KDC单进程多进程的对比,其处理瓶颈和AS一致,均受到CPU处理能力的限制(单进程打满某一CPU,多进程几乎占用整台机器的CPU资源)。从Kerberos的设计原理分析,很容易理解,无论KDC库中的keytab是否带有PREAUTH属性,对TGS的处理逻辑几乎没有影响,压测的数据结果从实际角度验证了这一点。

C:其它问题
Client和KDC的交互,支持TCP和UDP两种协议。在网络环境良好的情况下,两种协议的KDC的测试结果理论上和实际中几乎一致。但是在原生代码中,使用TCP协议,在客户端给KDC造成一定压力持续6s左右,客户端开始认证出错,在远未达到超时时限的情况下,Client出现了socket reset类的错误。KDC查看内核日志,发现大量possible SYN flooding on port 8089(KDC的服务端口). Sending cookies,且通过netstat -s发现机器的xxxx times the listen queue of a socket overflowed异常增高,种种现象表明可能是服务端的半连接队列、全连接队列中的一个或者全部被打满。主要原理如图10所示:

发现KDC服务所在服务器:半队列/proc/sys/net/ipv4/tcp_max_syn_backlog为2048。
全队列:1)系统参数/proc/sys/net/core/somaxconn=65535,查看代码listen()函数的传入值为5。
故而判断TCP的瓶颈在于全队列,因此目标为将listen函数的第二个backlog参数变成可控可传入。
KDC可监控的设计和实现
开源社区对Kerberos实现的KDC完全没有对外暴露可监控的接口,最初线上的场景主要通过检索Log进行相关指标的监控,在统计服务QPS、各种错误的监控等方面,存在准确准确监控难的尴尬局面。为了实现对KDC准确、较全面的监控,对KDC进行了二次开发,设计一个获取监控指标的接口。对监控的设计,主要从以下三个方面进行了考虑和设计。
A.设计上的权衡
监控的设计无论在什么场景下,都应该尽可能的不去或者最小程度的影响线上的服务,本文最终采用建立一块共享内存的方式,记录各个KDC进程的打点信息,实现的架构如图11所示。每个KDC进程对应共享内存中的一块区域,通过n个数组来存储KDC n个进程的服务指标:当某个KDC进程处理一个请求后,该请求对监控指标的影响会直接打点更新到其对应的Slot 数组中。更新的过程不受锁等待更新的影响,KDC对监控打点的调用仅仅是内存块中的更新,对服务的影响几乎可以忽略不计。相比其他方式,在实现上也更加简单、易理解。
纪录每个KDC进程的服务情况,便于准确查看每个进程的对请求的处理情况,有助于定位问题多种情况下出现的异常,缩短故障的定位时间。例如:能够准确的反应出每个进程的请求分布是否均匀、请求处理出现异常能够定位到具体是某个进程出现异常还是整体均有异常。
KDC监控设计的整体架构:

B.程序的可拓展性
任何指标的采集都是随着需求进行变更的,如果程序设计上不具有良好的扩展性,会后续的指标扩展带来很大的困扰。第一版KDC监控指标的采集只区分请求的成功与失败两种类型,美团数据平台KDC库中所有的keytab都具有PREAUTH属性。根据上文可知,去掉PREAUTH属性后,AS请求的QPS能够提升一倍。后续随着服务规模的进一步增长,如果AS请求的处理能力逐步成为瓶颈,会考虑去掉PREAUTH属性。为了准确监控去掉PREAUTH属性这一过程是否有、有多少请求出现错误,需要扩展一个监控指标,因此有了KDC监控的第二版。整个过程只需要修改三个地方,完成两个功能的实现:
- 添加指标 ;
- 打点逻辑的添加。
整个修改过程简单明了,因此,该KDC监控程序的设计具有非常好的扩展性。图12为监控指标的罗列和注释:
KDC监控指标及含义:

C.接口工具kstat的设计
获取KDC监控指标的接口工具主要分为两种:
- 获取当前每个KDC进程对各个指标的累积值,该功能是为了和新美大的监控平台Falcon结合,方便实现指标的上报实现累加值和分钟级别速率值的处理;
- 获取制定次数在制定时间间隔内每个进程监控指标的瞬时速率,最小统计间隔可达秒级,方便运维人员登陆机器无延迟的查看当前KDC的服务情况,使其在公司监控系统不可用的情况下分析服务的当前问题。具体使用见下图。
kstat的使用帮助和两种功能使用样例:

总结:
通过本次对KDC服务的压测实验和分析,总结出KDC最优性能的调整方案为:
- KDC服务本身需要开启多进程和以充分利用多核机器的CPU资源,同时确保BDB的内存资源足够,保证其缓存命中率达到一定比例(越高越好,否则查询库会带来大量的磁盘读IO);
- 选择的物理机要做RAID,否则在库中keytab带有PREAUTH属性的条件下,会带来大量的写,容易导致磁盘成为KDC的性能瓶颈。通过建立一块共享内存无锁的实现了KDC多进程指标的收集,加上其良好的扩展性和数据的精确性,极大的提高了KDC服务的可靠性。
相比原来线上单进程的处理能力,目前单台服务器的处理性能提升10+倍以上。本次工作没有详细的论述TCP协议中半队列、全队列的相关参数应该如何设定才能达到最优,和服务本身结合到一起,每个参数的变更带来的影响具体是什么因为过于复杂,还没有介绍。
美团数据安全平台建设实践 介绍了权限模型和解决方案等